package com.zmn.uac.business.impl.oauth.login;

import com.alibaba.fastjson.JSON;
import com.zmn.common.dto2.ResponseDTO;
import com.zmn.common.utils.string.StringUtil;
import com.zmn.consts.GlobalConsts;
import com.zmn.saas.dubbo.dto.app.AppDRO;
import com.zmn.uac.business.interfaces.oauth.login.KuaiShouLoginBService;
import com.zmn.uac.business.template.oauth.login.BaseLoginTemplateMode;
import com.zmn.uac.common.constant.AccessTypeConstant;
import com.zmn.uac.common.constant.KuaiShouConstant;
import com.zmn.uac.common.dio.oauth.login.TempCodeDIO;
import com.zmn.uac.common.dio.oauth.login.UserAuthLoginDIO;
import com.zmn.uac.common.entity.oauth.login.*;
import com.zmn.uac.common.entity.oauth.phone.UserPhoneInfo;
import com.zmn.uac.common.info.UserAuthPhoneInfo;
import com.zmn.uac.common.info.kuaishou.KuaiShouAuthPhoneInfo;
import com.zmn.uac.common.info.kuaishou.KuaiShouMiniProgramSessionInfo;
import com.zmn.uac.common.info.kuaishou.KuaiShouPhoneInfo;
import com.zmn.uac.util.HttpClientsUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.springframework.stereotype.Service;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.AlgorithmParameters;
import java.security.Security;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * 快手常量类
 *
 * @author Rananquan
 * @since 2022年5月16日 18点38分
 */
@Slf4j
@Service
public class KuaiShouLoginBServiceImpl extends BaseLoginTemplateMode implements KuaiShouLoginBService {
    private static final String TAG = "快手授权登陆实现";

    @Override
    public ResponseDTO<UserAuthLoginInfo> thirdPartyUserDataAcquisition(UserAuthLoginDIO dio, UserAuthPhoneInfo authPhoneInfo) {
        AppDRO app = Objects.equals(dio.getAccessType(), AccessTypeConstant.SHOP)
                ? getAppInfoByShopId(dio.getShopId()) : getAppByChannelId(dio.getChannelId());
        if (app == null) {
            return ResponseDTO.fail("查询应用为null");
        }

        KuaiShouAuthPhoneInfo kuaiShouAuthPhoneInfo = (KuaiShouAuthPhoneInfo) authPhoneInfo;

        ResponseDTO<KuaiShouMiniProgramSessionInfo> sessionInfoResponse = code2Session(app.getThirdAppId(), app.getThirdAppSecret(), kuaiShouAuthPhoneInfo.getCode(), dio.getUuid());
        if (!sessionInfoResponse.isSuccess()) {
            return ResponseDTO.fail(sessionInfoResponse.getMessage());
        }

        KuaiShouMiniProgramSessionInfo sessionInfo = sessionInfoResponse.getData();

        ResponseDTO<String> kuaiShouPhoneResponse = getKuaiShouPhone(kuaiShouAuthPhoneInfo, sessionInfo);
        if (!kuaiShouPhoneResponse.isSuccess()) {
            return ResponseDTO.fail(kuaiShouPhoneResponse.getMessage());
        }

        UserPhoneInfo userPhoneInfo = UserPhoneInfo.builder().mobile(kuaiShouPhoneResponse.getData()).build();
        UserInfo info = setUserInfo(sessionInfo);
        PlatInfo platInfo = setPlatInfo(dio, app);
        ThirdAppInfo thirdAppInfo = setThirdAppInfo(app);
        UserOtherInfo userOtherInfo = setUserOtherInfo(sessionInfo);
        UserAuthLoginInfo userAuthLoginInfo = setUserAuthLoginInfo(info, userOtherInfo, platInfo, thirdAppInfo, userPhoneInfo);

        return ResponseDTO.success(userAuthLoginInfo);
    }

    @Override
    public ResponseDTO<String> getThirdPartyOpenIdByCode(TempCodeDIO tempCodeDIO, AppDRO app) {
        ResponseDTO<KuaiShouMiniProgramSessionInfo> response = code2Session(app.getThirdAppId(), app.getThirdAppSecret(), tempCodeDIO.getCode(), null);
        if (!response.isSuccess()) {
            return ResponseDTO.fail(response.getMessage());
        }
        return ResponseDTO.success(response.getData().getOpenId());
    }

    private ResponseDTO<KuaiShouMiniProgramSessionInfo> code2Session(String appId, String secret, String code, String uuid) {
        log.info("[{}], {}, 获取用户小程序授权code信息, 请求参数：appId={},secret={},code={}", TAG, uuid, appId, secret, code);

        String jsonResult;
        try {
            Map<String, String> params = new HashMap<>(3);
            params.put("js_code", code);
            params.put("app_id", appId);
            params.put("app_secret", secret);

            jsonResult = HttpClientsUtil.executePost(KuaiShouConstant.CODE_2_SESSION_URL, params);
        } catch (Exception e) {
            log.error("[{}], {}, 获取用户小程序授权code信息异常", TAG, uuid, e);
            return ResponseDTO.fail("获取用户小程序授权code信息异常");
        }

        log.info("[{}], {}, 获取用户小程序授权code信息, 响应结果：{}", TAG, uuid, jsonResult);

        KuaiShouMiniProgramSessionInfo sessionInfo = JSON.parseObject(jsonResult, KuaiShouMiniProgramSessionInfo.class);

        return sessionInfo.isSuccess() ? ResponseDTO.success(sessionInfo) : ResponseDTO.fail("获取用户小程序授权code信息失败");
    }

    private ResponseDTO<String> getKuaiShouPhone(KuaiShouAuthPhoneInfo authPhoneInfo, KuaiShouMiniProgramSessionInfo sessionInfo) {
        if (StringUtil.isNotBlank(authPhoneInfo.getMobile())) {
            return ResponseDTO.success(authPhoneInfo.getMobile());
        }

        ResponseDTO<KuaiShouPhoneInfo> phoneInfoResponse = decrypt(authPhoneInfo.getEncryptedData(), authPhoneInfo.getIv(), sessionInfo.getSessionKey());
        if (!phoneInfoResponse.isSuccess()) {
            return ResponseDTO.fail(phoneInfoResponse.getMessage());
        }

        return ResponseDTO.success(phoneInfoResponse.getData().purePhoneNumber());
    }

    private ResponseDTO<KuaiShouPhoneInfo> decrypt(String encryptedData, String iv, String sessionKey) {
        try {
            Security.addProvider(new BouncyCastleProvider());
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding", "BC");
            SecretKeySpec spec = new SecretKeySpec(toByte(sessionKey), "AES");
            AlgorithmParameters parameters = AlgorithmParameters.getInstance("AES");
            parameters.init(new IvParameterSpec(toByte(iv)));
            cipher.init(Cipher.DECRYPT_MODE, spec, parameters);
            byte[] resultByte = cipher.doFinal(padding(toByte(encryptedData)));
            if (null != resultByte && resultByte.length > 0) {
                String result = new String(resultByte, StandardCharsets.UTF_8);
                log.info("[{}], 解密用户数据结果：{}", TAG, result);
                return ResponseDTO.success(JSON.parseObject(result, KuaiShouPhoneInfo.class));
            } else {
                return ResponseDTO.fail("解密数据为null");
            }
        } catch (Exception e) {
            log.error("[{}], 解密数据异常", TAG, e);
            return ResponseDTO.fail("解密数据异常");
        }
    }

    private byte[] toByte(String base64) {
        return Base64.decodeBase64(base64);
    }

    private byte[] padding(byte[] src) {
        int base = 16;
        if (src.length % base != 0) {
            int groups = src.length / base + 1;
            byte[] temp = new byte[groups * base];
            Arrays.fill(temp, (byte) 0);
            System.arraycopy(src, 0, temp, 0, src.length);
            return temp;
        }
        return src;
    }

    private UserInfo setUserInfo(KuaiShouMiniProgramSessionInfo sessionInfo){
        return UserInfo.builder()
                .userId(null)
                .openId(sessionInfo.getOpenId())
                .unionId(sessionInfo.getOpenId())
                .type(GlobalConsts.NO)
                .realname("快手用户")
                .username(null)
                .nickName(null)
                .avatarSrc(null)
                .age(null)
                .gender(3)
                .country(null)
                .province(null)
                .city(null)
                .build();
    }

    private PlatInfo setPlatInfo(UserAuthLoginDIO dio,AppDRO app){
        return PlatInfo.builder()
                .shopId(dio.getShopId())
                .plat(app.getPlat())
                .channelId(dio.getChannelId())
                .channelName(getNameByChannelId(dio.getChannelId()))
                .entrance(dio.getEntrance())
                .build();
    }

    private ThirdAppInfo setThirdAppInfo(AppDRO app){
        return ThirdAppInfo.builder()
                .appId(app.getThirdAppId())
                .appName(app.getName())
                .appSecret(app.getThirdAppSecret())
                .build();
    }

    private UserOtherInfo setUserOtherInfo(KuaiShouMiniProgramSessionInfo sessionInfo){
        return UserOtherInfo.builder()
                .language(null)
                .sessionKey(sessionInfo.getSessionKey())
                .build();
    }

    private UserAuthLoginInfo setUserAuthLoginInfo(UserInfo info,UserOtherInfo userOtherInfo,PlatInfo platInfo,ThirdAppInfo thirdAppInfo,UserPhoneInfo userPhoneInfo){
        return UserAuthLoginInfo.builder()
                .userInfo(info)
                .userOtherInfo(userOtherInfo)
                .platInfo(platInfo)
                .thirdAppInfo(thirdAppInfo)
                .userPhoneInfo(userPhoneInfo)
                .build();
    }
}
